(function ($, window, document) {
    var mid = 0;
    $.Lazyload = $.Class.extend({
        init: function (element, options) {
            var self = this;
            this.container = this.element = element;
//			placeholder //默认图片
            this.options = $.extend({
                selector: '',//查询哪些元素需要lazyload
                diff: false,//距离视窗底部多少像素出发lazyload
                force: false,//强制加载(不论元素是否在是视窗内)
                autoDestroy: true,//元素加载完后是否自动销毁当前插件对象
                duration: 100//滑动停止多久后开始加载
            }, options);

            this._key = 0;
            this._containerIsNotDocument = this.container.nodeType !== 9;
            this._callbacks = {};

            this._init();
        },
        _init: function () {
            this._initLoadFn();

            this.addElements();

            this._loadFn();

            $.ready(function () {
                this._loadFn();
            }.bind(this));

            this.resume();
        },
        _initLoadFn: function () {
            var self = this;
            self._loadFn = this._buffer(function () { // 加载延迟项
                if (self.options.autoDestroy && self._counter == 0 && $.isEmptyObject(self._callbacks)) {
                    self.destroy();
                }
                self._loadItems();
            }, self.options.duration, self);
        },
        /**
         *根据加载函数实现加载器
         *@param {Function} load 加载函数
         *@returns {Function} 加载器
         */
        _createLoader: function (load) {
            var value, loading, handles = [],
                h;
            return function (handle) {
                if (!loading) {
                    loading = true;
                    load(function (v) {
                        value = v;
                        while (h = handles.shift()) {
                            try {
                                h && h.apply(null, [value]);
                            } catch (e) {
                                setTimeout(function () {
                                    throw e;
                                }, 0)
                            }
                        }
                    })
                }
                if (value) {
                    handle && handle.apply(null, [value]);
                    return value;
                }
                handle && handles.push(handle);
                return value;
            }
        },
        _buffer: function (fn, ms, context) {
            var timer;
            var lastStart = 0;
            var lastEnd = 0;
            var ms = ms || 150;

            function run() {
                if (timer) {
                    timer.cancel();
                    timer = 0;
                }
                lastStart = $.now();
                fn.apply(context || this, arguments);
                lastEnd = $.now();
            }

            return $.extend(function () {
                if (
                    (!lastStart) || // 从未运行过
                    (lastEnd >= lastStart && $.now() - lastEnd > ms) || // 上次运行成功后已经超过ms毫秒
                    (lastEnd < lastStart && $.now() - lastStart > ms * 8) // 上次运行或未完成，后8*ms毫秒
                ) {
                    run();
                } else {
                    if (timer) {
                        timer.cancel();
                    }
                    timer = $.later(run, ms, null, arguments);
                }
            }, {
                stop: function () {
                    if (timer) {
                        timer.cancel();
                        timer = 0;
                    }
                }
            });
        },
        _getBoundingRect: function (c) {
            var vh, vw, left, top;

            if (c !== undefined) {
                vh = c.offsetHeight;
                vw = c.offsetWidth;
                var offset = $.offset(c);
                left = offset.left;
                top = offset.top;
            } else {
                vh = window.innerHeight;
                vw = window.innerWidth;
                left = 0;
                top = window.pageYOffset;
            }

            var diff = this.options.diff;

            var diffX = diff === false ? vw : diff;
            var diffX0 = 0;
            var diffX1 = diffX;

            var diffY = diff === false ? vh : diff;
            var diffY0 = 0;
            var diffY1 = diffY;

            var right = left + vw;
            var bottom = top + vh;


            left -= diffX0;
            right += diffX1;
            top -= diffY0;
            bottom += diffY1;
            return {
                left: left,
                top: top,
                right: right,
                bottom: bottom
            };
        },
        _cacheWidth: function (el) {
            if (el._mui_lazy_width) {
                return el._mui_lazy_width;
            }
            return el._mui_lazy_width = el.offsetWidth;
        },
        _cacheHeight: function (el) {
            if (el._mui_lazy_height) {
                return el._mui_lazy_height;
            }
            return el._mui_lazy_height = el.offsetHeight;
        },
        _isCross: function (r1, r2) {
            var r = {};
            r.top = Math.max(r1.top, r2.top);
            r.bottom = Math.min(r1.bottom, r2.bottom);
            r.left = Math.max(r1.left, r2.left);
            r.right = Math.min(r1.right, r2.right);
            return r.bottom >= r.top && r.right >= r.left;
        },
        _elementInViewport: function (elem, windowRegion, containerRegion) {
            // display none or inside display none
            if (!elem.offsetWidth) {
                return false;
            }
            var elemOffset = $.offset(elem);
            var inContainer = true;
            var inWin;
            var left = elemOffset.left;
            var top = elemOffset.top;
            var elemRegion = {
                left: left,
                top: top,
                right: left + this._cacheWidth(elem),
                bottom: top + this._cacheHeight(elem)
            };

            inWin = this._isCross(windowRegion, elemRegion);

            if (inWin && containerRegion) {
                inContainer = this._isCross(containerRegion, elemRegion);
            }
            // 确保在容器内出现
            // 并且在视窗内也出现
            return inContainer && inWin;
        },
        _loadItems: function () {
            var self = this;
            // container is display none
            if (self._containerIsNotDocument && !self.container.offsetWidth) {
                return;
            }
            self._windowRegion = self._getBoundingRect();

            if (self._containerIsNotDocument) {
                self._containerRegion = self._getBoundingRect(this.container);
            }
            $.each(self._callbacks, function (key, callback) {
                callback && self._loadItem(key, callback);
            });
        },
        _loadItem: function (key, callback) {
            var self = this;
            callback = callback || self._callbacks[key];
            if (!callback) {
                return true;
            }
            var el = callback.el;
            var remove = false;
            var fn = callback.fn;
            if (self.options.force || self._elementInViewport(el, self._windowRegion, self._containerRegion)) {
                try {
                    remove = fn.call(self, el, key);
                } catch (e) {
                    setTimeout(function () {
                        throw e;
                    }, 0);
                }
            }
            if (remove !== false) {
                delete self._callbacks[key];
            }
            return remove;
        },
        addCallback: function (el, fn) {
            var self = this;
            var callbacks = self._callbacks;
            var callback = {
                el: el,
                fn: fn || $.noop
            };
            var key = ++this._key;
            callbacks[key] = callback;

            // add 立即检测，防止首屏元素问题
            if (self._windowRegion) {
                self._loadItem(key, callback);
            } else {
                self.refresh();
            }
        },
        addElements: function (elements) {
            var self = this;
            self._counter = self._counter || 0;
            var lazyloads = [];
            if (!elements && self.options.selector) {
                lazyloads = self.container.querySelectorAll(self.options.selector);
            } else {
                $.each(elements, function (index, el) {
                    lazyloads = lazyloads.concat($.qsa(self.options.selector, el));
                });
            }
            $.each(lazyloads, function (index, el) {
                if (!el.getAttribute('data-lazyload-id')) {
                    if (self.addElement(el)) {
                        el.setAttribute('data-lazyload-id', mid++);
                        self.addCallback(el, self.handle);
                    }
                }
            });
        },
        addElement: function (el) {
            return true;
        },
        handle: function () {
            //throw new Error('需子类实现');
        },
        refresh: function (check) {
            if (check) { //检查新的lazyload
                this.addElements();
            }
            this._loadFn();
        },
        pause: function () {
            var load = this._loadFn;
            if (this._destroyed) {
                return;
            }
            window.removeEventListener('scroll', load);
            window.removeEventListener($.EVENT_MOVE, load);
            window.removeEventListener('resize', load);
            if (this._containerIsNotDocument) {
                this.container.removeEventListener('scrollend', load);
                this.container.removeEventListener('scroll', load);
                this.container.removeEventListener($.EVENT_MOVE, load);
            }
        },
        resume: function () {
            var load = this._loadFn;
            if (this._destroyed) {
                return;
            }
            window.addEventListener('scroll', load, false);
            window.addEventListener($.EVENT_MOVE, load, false);
            window.addEventListener('resize', load, false);
            if (this._containerIsNotDocument) {
                this.container.addEventListener('scrollend', load, false);
                this.container.addEventListener('scroll', load, false);
                this.container.addEventListener($.EVENT_MOVE, load, false);
            }
        },
        destroy: function () {
            var self = this;
            self.pause();
            self._callbacks = {};
            $.trigger(this.container, 'destroy', self);
            self._destroyed = 1;
        }
    });
})(mui, window, document);